﻿using System.Runtime.InteropServices;
using System;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Web;
using DDW.Swf;
using DotNetNuke.Entities.Portals;
using Telerik.Web.UI;

namespace Findy.XsltDb
{
    /// <summary>
    /// Summary description for ImageTools
    /// </summary>
    public class ImageTools
    {
        public static bool GetSwfDimension(string fileName, out int width, out int height)
        {
            try
            {
                SwfHeader h = new SwfHeader(new SwfReader(File.ReadAllBytes(fileName)));
                width = (h.FrameSize.XMax - h.FrameSize.XMin) / 20;
                height = (h.FrameSize.YMax - h.FrameSize.YMin) / 20;
                return true;
            }
            catch (Exception)
            {
                width = height = 0;
                return false;
            }
        }
        public static bool GetJpegDimension(string fileName, out int width, out int height)
        {

            width = height = 0;
            bool found = false;
            bool eof = false;

            try
            {

                using (FileStream stream = File.OpenRead(fileName))
                {

                    BinaryReader reader = new BinaryReader(stream);

                    while (!found || eof)
                    {

                        // read 0xFF and the type
                        reader.ReadByte();
                        byte type = reader.ReadByte();

                        // get length
                        int len = 0;
                        switch (type)
                        {
                            // start and end of the image
                            case 0xD8:
                            case 0xD9:
                                len = 0;
                                break;

                            // restart interval
                            case 0xDD:
                                len = 2;
                                break;

                            // the next two bytes is the length
                            default:
                                int lenHi = reader.ReadByte();
                                int lenLo = reader.ReadByte();
                                len = (lenHi << 8 | lenLo) - 2;
                                break;
                        }

                        // EOF?
                        if (type == 0xD9)
                            eof = true;

                        // process the data
                        if (len > 0)
                        {

                            // read the data
                            byte[] data = reader.ReadBytes(len);

                            // this is what we are looking for
                            if (type == 0xC0)
                            {
                                height = data[1] << 8 | data[2];
                                width = data[3] << 8 | data[4];
                                found = true;
                            }

                        }

                    }

                    return found;
                }
            }
            catch { }
            return false;
        }

        public static bool GetFlvDimension(string fileName, out int width, out int height)
        {
            width = height = 0;
            using (FileStream fs = File.OpenRead(fileName))
            {
                byte[] bsize = { 0, 0, 0, 0 };
                fs.Seek(14, SeekOrigin.Begin);
                fs.Read(bsize, 1, 3);
                Array.Reverse(bsize);
                int headerSize = BitConverter.ToInt32(bsize, 0);
                fs.Seek(0, SeekOrigin.Begin);
                byte[] bytes = new byte[headerSize];
                fs.Read(bytes, 0, headerSize);
                string marker = Encoding.ASCII.GetString(bytes, 27, 10);
                if (marker != "onMetaData")
                    return false;

                string s = Encoding.ASCII.GetString(bytes);
                int wp = s.IndexOf("width\0") + 6;
                Array.Reverse(bytes, wp, 8);
                width = Convert.ToInt32(BitConverter.ToDouble(bytes, wp));
                int hp = s.IndexOf("height\0") + 7;
                Array.Reverse(bytes, hp, 8);
                height = Convert.ToInt32(BitConverter.ToDouble(bytes, hp));
            }
            return true;
        }


        public static string CropImage(string fullPath, int x, int y, int w, int h)
        {
            string id = fullPath + "|" + x + "|" + y + "|" + w + "|" + h;
            string hash = id.MD5();
            string cropDir = fullPath + ".thumb";
            string cropPath = cropDir + "\\" + id.MD5() +".jpg";
            if (!File.Exists(cropPath))
            {
                lock (typeof(ImageTools))
                {
                    if (!File.Exists(cropPath))
                    {
                        if (!Directory.Exists(cropDir))
                            Directory.CreateDirectory(cropDir);
                        var bmp = LoadBitmap(fullPath);
                        var newBmp = bmp.Clone(new Rectangle(x, y, w, h), bmp.PixelFormat);
                        newBmp.Save(cropPath, ImageFormat.Jpeg);
                    }
                }
            }
            return cropPath;
        }

        public static Thumbnail CreateThumbnail(ThumbnailArgs args)
        {
            if (args == null) return null;
            if (string.IsNullOrEmpty(args.OriginalPath)) return null;
            if (!File.Exists(args.OriginalPath)) return null;

            if (string.IsNullOrEmpty(args.Format)) args.Format = "jpeg";
            if (args.Quality == 0) args.Quality = 80;

            if (args.IsPersistent)
                return CreatePersistentThumbnail(args);
            else
                return CreateMemoryThumbnail(args);
        }

        public static Thumbnail CreateMemoryThumbnail(ThumbnailArgs args)
        {
            ImageCodecInfo codec = CreateEncoder(args.Format);

            var p = new EncoderParameters(1);
            p.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, args.Quality);

            Bitmap original;
            using (var fr = File.OpenRead(args.OriginalPath))
                original = (Bitmap)Image.FromStream(fr);

            decimal k = 1;
            decimal oH = args.y1 > 0 && args.y2 > 0 ? args.y2 - args.y1 : original.Height;
            decimal oW = args.x1 > 0 && args.x2 > 0 ? args.x2 - args.x1 : original.Width;
            if (args.Width > 0 && args.Height > 0)
                k = Math.Min(args.Height / oH, args.Width / oW);
            else if (args.Width > 0 || args.Height > 0)
                k = Math.Max(args.Height / oH, args.Width / oW);
            k = Math.Min(k, 1);
            var bmp = new Bitmap((int)Math.Round(k * oW), (int)Math.Round(k * oH), PixelFormat.Format32bppArgb);

            using (var g = Graphics.FromImage(bmp))
            {
                if (args.Format == "jpeg" || args.Format == "gif")
                    g.FillRectangle(Brushes.White, 0, 0, bmp.Width, bmp.Height);
                g.SmoothingMode = SmoothingMode.AntiAlias;
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                g.PixelOffsetMode = PixelOffsetMode.HighQuality;

                if (args.x1 > 0 && args.y1 > 0 && args.x2 > 0 && args.y2 > 0)
                    g.DrawImage(original, new Rectangle(0,0,bmp.Width, bmp.Height),
                        new Rectangle(args.x1, args.y1, args.x2-args.x1,args.y2-args.y1), GraphicsUnit.Pixel);
                else
                    g.DrawImage(original, 0, 0, bmp.Width, bmp.Height);

                if (args.customDraw != null)
                    args.customDraw(g, original, args);

                if (!string.IsNullOrEmpty(args.WatermarkText))
                {
                    StringFormat drawFormat = new StringFormat();
                    drawFormat.FormatFlags = StringFormatFlags.NoWrap;
                    int size = 3 * bmp.Width / args.WatermarkText.Length / 4;
                    int x = (bmp.Width - size * args.WatermarkText.Length) / 2;
                    int y = bmp.Height / 2 - 3 * size / 4;
                    g.DrawString(
                        args.WatermarkText,
                        new Font("Courier New", size, FontStyle.Bold),
                        new SolidBrush(Color.FromArgb(100, 0, 0, 0)),
                        new Point(x, y));
                }

                if (!string.IsNullOrEmpty(args.WatermarkPath))
                {
                    Image wm;
                    using (FileStream wfs = File.OpenRead(args.WatermarkPath))
                        wm = Image.FromStream(wfs);
                    Bitmap bm = (Bitmap)wm;
                    Bitmap bm2 = new Bitmap(wm.Width, bm.Height);

                    for (int i = 0; i < bm.Width; i++)
                        for (int j = 0; j < bm.Height; j++)
                            bm2.SetPixel(i, j, Color.FromArgb(100, bm.GetPixel(i, j)));

                    g.CompositingQuality = CompositingQuality.GammaCorrected;
                    g.CompositingMode = CompositingMode.SourceOver;
                    g.DrawImage(bm2, bmp.Width - wm.Width - 10, bmp.Height - wm.Height - 10);
                }
            }
            if ( !string.IsNullOrEmpty(args.Breaker))
            {
                Bitmap rightPart = null;
                string Breaker = args.Breaker;
                int rightX = int.Parse(Breaker[0].ToString());
                int rightY = int.Parse(Breaker[2].ToString());

                int mX = int.Parse(args.Breaker[1].ToString());
                int mY = int.Parse(args.Breaker[3].ToString());
                for (int pX = 0; pX < mX; pX++)
                {
                    for (int pY = 0; pY < mY; pY++)
                    {
                        int x1 = bmp.Width * pX / mX;
                        int y1 = bmp.Height * pY / mY;

                        args.Breaker = pX.ToString() + mX.ToString() + pY.ToString() + mY.ToString();

                        Bitmap part = new Bitmap(
                            bmp.Width / mX + (pX + 1 == mX ? bmp.Width % mX : 0),
                            bmp.Height / mY + (pY + 1 == mY ? bmp.Height % mY : 0)
                        );
                        using (Graphics pg = Graphics.FromImage(part))
                        {
                            pg.DrawImage(bmp,
                                new Rectangle(0, 0, part.Width, part.Height),
                                new Rectangle(x1, y1, part.Width, part.Height),
                                GraphicsUnit.Pixel);
                        }

                        if (!Directory.Exists(args.ThumbDir))
                            Directory.CreateDirectory(args.ThumbDir);

                        MemoryStream pMs = new MemoryStream();
                        part.Save(pMs, codec, p);
                        byte[] bPart = pMs.ToArray();

                        using (FileStream fs = File.Create(args.ThumbPath))
                            fs.Write(bPart, 0, bPart.Length);
                        if (rightX == pX && rightY == pY)
                            rightPart = part;
                    }
                }

                bmp = rightPart;
                args.Breaker = Breaker;
            }

            if (args.Format == "gif")
                bmp = MakeTransparentGif(bmp, Color.White);

            var ms = new MemoryStream();
            bmp.Save(ms, codec, p);
            return new Thumbnail(ms.ToArray());
        }

        static Dictionary<string, ImageCodecInfo> icTable;
        static ImageCodecInfo CreateEncoder(string format)
        {
            if (icTable == null)
            {
                lock (typeof(ImageTools))
                {
                    if (icTable == null)
                    {

                        Dictionary<Guid, string> ifTable = new Dictionary<Guid, string>();
                        ifTable[ImageFormat.Jpeg.Guid] = "jpeg";
                        ifTable[ImageFormat.Png.Guid] = "png";
                        ifTable[ImageFormat.Bmp.Guid] = "bmp";
                        ifTable[ImageFormat.Tiff.Guid] = "tiff";
                        ifTable[ImageFormat.Gif.Guid] = "gif";

                        icTable = new Dictionary<string, ImageCodecInfo>();
                        ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
                        foreach (ImageCodecInfo codec in codecs)
                            if (ifTable.ContainsKey(codec.FormatID))
                                icTable[ifTable[codec.FormatID]] = codec;
                    }
                }
            }
            return icTable[format];
        }

        private static Dictionary<ushort, string> Locks = new Dictionary<ushort, string>();
        public static Thumbnail CreatePersistentThumbnail(ThumbnailArgs args)
        {
            string thumbDir = args.ThumbDir;
            string thumbPath = args.ThumbPath;
            if (File.Exists(thumbPath))
                return new Thumbnail(thumbPath);

            string syncLock = null;
            ushort crc = args.ComputeHash().Crc16();
            lock (typeof(ImageTools))
            {
                if (!Locks.ContainsKey(crc))
                    Locks[crc] = crc.ToString();
                syncLock = Locks[crc];
            }

            lock (syncLock)
            {
                if (File.Exists(thumbPath))
                    return new Thumbnail(thumbPath);

                Thumbnail t = CreateMemoryThumbnail(args);

                if (!Directory.Exists(thumbDir))
                    Directory.CreateDirectory(thumbDir);

                using (FileStream fs = File.Create(thumbPath))
                    fs.Write(t.ThumbnailBytes, 0, t.ThumbnailBytes.Length);
                t.ThumbnailPath = thumbPath;
                return t;
            }
        }

        public static Bitmap LoadBitmap(string fullPath)
        {
            using (FileStream fr = File.OpenRead(fullPath))
                return new Bitmap(fr);
        }

    /// <summary>
    /// Returns a transparent background GIF image from the specified Bitmap.
    /// </summary>
    /// <param name="bitmap">The Bitmap to make transparent.</param>
    /// <param name="color">The Color to make transparent.</param>
    /// <returns>New Bitmap containing a transparent background gif.</returns>
    static Bitmap MakeTransparentGif(Bitmap bitmap, Color color)
    {
        byte R = color.R;
        byte G = color.G;
        byte B = color.B;
        MemoryStream fin = new MemoryStream();
        bitmap.Save(fin, System.Drawing.Imaging.ImageFormat.Gif);
        MemoryStream fout = new MemoryStream((int)fin.Length);
        int count = 0;
        byte[] buf = new byte[256];
        byte transparentIdx = 0;
        fin.Seek(0, SeekOrigin.Begin);
        //header
        count = fin.Read(buf, 0, 13);
        if ((buf[0] != 71) || (buf[1] != 73) || (buf[2] != 70)) return null; //GIF
        fout.Write(buf, 0, 13);
        int i = 0;
        if ((buf[10] & 0x80) > 0)
        {
            i = 1 << ((buf[10] & 7) + 1) == 256 ? 256 : 0;
        }
        for (; i != 0; i--)
        {
            fin.Read(buf, 0, 3);
            if ((buf[0] == R) && (buf[1] == G) && (buf[2] == B))
            {
                transparentIdx = (byte)(256 - i);
            }
            fout.Write(buf, 0, 3);
        }
        bool gcePresent = false;
        while (true)
        {
            fin.Read(buf, 0, 1);
            fout.Write(buf, 0, 1);
            if (buf[0] != 0x21) break;
            fin.Read(buf, 0, 1);
            fout.Write(buf, 0, 1);
            gcePresent = (buf[0] == 0xf9);
            while (true)
            {
                fin.Read(buf, 0, 1);
                fout.Write(buf, 0, 1);
                if (buf[0] == 0) break;
                count = buf[0];
                if (fin.Read(buf, 0, count) != count) return null;
                if (gcePresent)
                {
                    if (count == 4)
                    {
                        buf[0] |= 0x01;
                        buf[3] = transparentIdx;
                    }
                }
                fout.Write(buf, 0, count);
            }
        }
        while (count > 0)
        {
            count = fin.Read(buf, 0, 1);
            fout.Write(buf, 0, 1);
        }
        fin.Close();
        fout.Flush();
        return new Bitmap(fout);
    }

        public class ThumbnailArgs
        {
            public delegate void customDrawDelegate(Graphics g, Bitmap original, ThumbnailArgs args);
            public customDrawDelegate customDraw;
            public string OriginalPath;
            public decimal Width;
            public decimal Height;
            public string WatermarkText;
            public string WatermarkPath;
            public Color Background;
            public bool IsPersistent;
            public int Quality = 80;
            public string Breaker;
            public string Format = "jpeg";

            public int x1, y1, x2, y2;

            public string ComputeHash()
            {
                StringBuilder sb = new StringBuilder();
                if (!string.IsNullOrEmpty(WatermarkPath))
                {
                    MD5 md5 = MD5.Create();
                    using (FileStream fs = File.OpenRead(WatermarkPath))
                        foreach (byte b in md5.ComputeHash(fs))
                            sb.Append(b).Append(".");
                }
                sb.Append(OriginalPath ?? string.Empty).Append(".");
                sb.Append(Width).Append(".");
                sb.Append(Height).Append(".");
                sb.Append(Quality).Append(".");
                sb.Append(WatermarkText ?? string.Empty).Append(".");

                sb.Append(x1).Append(".");
                sb.Append(y1).Append(".");
                sb.Append(x2).Append(".");
                sb.Append(y2).Append(".");

                sb.Append(Format ?? string.Empty).Append(".");

                return sb.ToString().MD5();
            }

            public string ThumbDir
            {
                get {
                    return OriginalPath + ".thumb";
                }
            }

            public string ThumbPath
            {
                get {
                    string hash = ComputeHash();
                    if (!string.IsNullOrEmpty(Breaker))
                        hash += "_" + Breaker;
                    return ThumbDir + "\\" + hash + "." + Format;
                }
            }

        }
        public class Thumbnail
        {
            public string ThumbnailPath;
            public byte[] ThumbnailBytes;

            public Thumbnail(string path){
                ThumbnailPath = path;
                ThumbnailBytes = File.ReadAllBytes(path);
            }
            public Thumbnail(byte[] data){ThumbnailBytes = data; }
        }
    }

    public class XsltDbCaptcha
    {
        public byte[] Bytes { get; set; }
        public string Text { get; set; }
        public string ID { get; set; }

        public static string CreateCacheKey(string id)
        {
            return "captcha-image:" + id;
        }
    }
}